package m3f.matrix;

import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Set;
import java.util.StringTokenizer;

public class SparseVector {
	private Hashtable<Integer, Double> hash;
	
	public SparseVector(){
		hash = new Hashtable<Integer, Double>();
	}
	
	public SparseVector addScalar(double s){
		SparseVector v = new SparseVector();
		Enumeration<Integer> enumKeys = hash.keys();
		while(enumKeys.hasMoreElements()){
			int key = enumKeys.nextElement();
			v.setQuick(key, hash.get(key)+s);
		}
		return v;		
	}
	
	public SparseVector addVectors(SparseVector other){
		SparseVector r = new SparseVector();
		Set<Integer> keys = hash.keySet();
		keys.addAll( other.hash.keySet() );
		for(Integer k: keys){
			r.setQuick(k, this.get(k) + other.get(k));
		}		
		return r;
	}
	
	public double dotProduct(SparseVector v){
		double result = 0.0;
		Enumeration<Integer> keys = hash.keys();
		while(keys.hasMoreElements()){
			int key = keys.nextElement();
			result += hash.get(key)*v.get(key);
		}
		return result;
	}
	
	public double get(int k){
		Double v = hash.get(k);
		if(v == null){
			return 0.0D;
		}
		return v;
	}
	
	public Enumeration<Integer> getNonZeros(){
		return hash.keys();
	}
	
	public void normalizeL1(){
		double norm = normL1();
		Enumeration<Integer> keys = hash.keys();
		while(keys.hasMoreElements()){
			int key = keys.nextElement();
			double value = hash.get(key);
			hash.put(key, value/norm);
		}
	}
	
	public double normL1(){
		double result = 0.0;
		Collection<Double> values = hash.values();
		for(Double v: values){
			result += Math.abs(v);
		}
		return result;		
	}
	
	public double normL2(){
		double result = 0.0;
		Collection<Double> values = hash.values();
		for(Double v: values){
			result += v*v;
		}
		return Math.sqrt(result);
	}
	
	public SparseVector scalarProduct(double s){
		SparseVector v = new SparseVector();
		Enumeration<Integer> keys = hash.keys();
		while(keys.hasMoreElements()){
			int key = keys.nextElement();
			v.setQuick(key, hash.get(key) );
		}
		return v;
	}
	
	public void setQuick(int k, double v){
		if(v != 0.0D){
			hash.put(k, v);
		}else{
			hash.remove(k);
		}
	}

	public String toTextRepresentation() {
		StringBuffer s = new StringBuffer();
		Enumeration<Integer> keys = hash.keys();
		while(keys.hasMoreElements()){
			int key = keys.nextElement();
			s.append(key);
			s.append(":");
			s.append(hash.get(key));
			s.append(" ");			
		}
		return s.toString().trim();
	}

	public void fromTextRepresentation(String textRepresentation) {
		StringTokenizer stk = new StringTokenizer(textRepresentation);
		while(stk.hasMoreTokens()){
			String cell[] = stk.nextToken().split(":");
			this.setQuick(Integer.valueOf(cell[0]), Double.valueOf(cell[1]));
		}
	}
    
    public int size(){
    	return this.hash.size();
    }
/*	private ArrayList<Integer> keys;
	private ArrayList<Double> values;
	
	public SparseVector(){
		keys = new ArrayList<Integer>();
		values = new ArrayList<Double>();
	}
	
	public SparseVector addScalar(double s){
		SparseVector v = new SparseVector();
		for(int i = 0; i < keys.size(); i++){
			v.keys.add( keys.get(i) );
			v.values.add( values.get(i) + s );
		}
		return v;		
	}
	
	public SparseVector addVectors(SparseVector other){
		SparseVector r = new SparseVector();
		int pointerA = 0, pointerB = 0;
		while(pointerA < keys.size() && pointerB < other.keys.size()){
			int kA = keys.get(pointerA), kB = other.keys.get(pointerB);
			double vA = values.get(pointerA), vB = other.values.get(pointerB);
			if(kA == kB){
				r.keys.add(kA);
				r.values.add( vA + vB );
				pointerA++;
				pointerB++;
			}
			else if(kA < kB){
				r.keys.add(kA);
				r.values.add(vA);
				pointerA ++;
			}
			else{
				r.keys.add(kB);
				r.values.add(vB);
				pointerB ++;
			}
		}
		while(pointerA < keys.size()){
			r.keys.add(keys.get(pointerA));
			r.values.add(values.get(pointerA));
			pointerA ++;
		}
		while(pointerB < other.keys.size()){
			r.keys.add(keys.get(pointerB));
			r.values.add(values.get(pointerB));
			pointerB ++;
		}
		
		return r;
	}
	
	public double dotProduct(SparseVector v){
		int pointerA = 0;
		int pointerB = 0;
		double result = 0.0;
		
		while(pointerA < this.keys.size() && pointerB < v.keys.size()){
			if(this.keys.get(pointerA) == v.keys.get(pointerB)){
				result += this.values.get(pointerA)*v.values.get(pointerB);
				pointerA++;
				pointerB++;
			}
			else{
				if(this.keys.get(pointerA) > v.keys.get(pointerB))
					pointerB++;
				else
					pointerA++;
			}
		}
		return result;
	}
	
	public double get(int k){
		int v = Collections.binarySearch(keys, k);
		if(v < 0)
			return 0.0;
		else
			return values.get(v);
	}
	
	public Iterator<Integer> getIndices(){
		return keys.iterator();
	}
	
	public void normalizeL1(){
		double norm = normL1();
		for(int i = 0; i < values.size(); i++){
			values.set(i, values.get(i)/norm);
		}
	}
	
	public double normL1(){
		double result = 0.0;
		for(int i = 0; i < values.size(); i++){
			result += Math.abs(values.get(i));
		}
		return result;		
	}
	
	public double normL2(){
		double result = 0.0;
		for(int i = 0; i < values.size(); i++){
			result += values.get(i)*values.get(i);
		}
		return Math.sqrt(result);
	}
	
	public SparseVector scalarProduct(double s){
		SparseVector v = new SparseVector();
		for(int i = 0; i < keys.size(); i++){
			v.keys.add( keys.get(i) );
			v.values.add( values.get(i)*s );
		}
		return v;
	}
	
	public void set(int k, double v){
		int idx = Collections.binarySearch(keys, k);
		if(v != 0.0 && idx < 0){
			// Add new key-value pair
			keys.add(k);
			values.add(v);
		}
		else if(v != 0.0 && idx >= 0){
			// Update an existent value
			values.set(Collections.binarySearch(keys, k), v);
		}
		else if(v == 0.0 && idx >= 0){
			// Remove the value that now is zero
			values.remove(keys.indexOf(k));
			keys.remove(keys.indexOf(k));
		}
		//sort();
	}
	
	public void setQuick(int k, double v){
		keys.add(k);
		values.add(v);
	}
	
	public void sort(){
		ArrayList<Integer> copyK = new ArrayList<Integer>();
		ArrayList<Double> copyV = new ArrayList<Double>();
		for(Integer k: keys){
			copyK.add(k);
			copyV.add(0.0);
		}
		Collections.sort(keys);
		for(int i = 0; i < copyK.size(); i++){
			int key = copyK.get(i);
			copyV.set(keys.indexOf(key), values.get(i));
		}
		values = copyV;
	}
	
	public String toString(){
		String s = "[ ";
		for(int i = 0; i < keys.size(); i++){
			s += "("+keys.get(i)+":"+values.get(i)+") ";
		}
		return s+"]";
	}*/
}
